package com.whimscrew.zuul.filters;


import com.google.common.io.CharStreams;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.math.RandomUtils;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPatch;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicHttpRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

//@Component
public class DynamicRoutesFilter extends ZuulFilter {
    private static final int FILTER_ORDER =  1;
    private static final boolean SHOULD_FILTER =true;


    private static final Logger logger = LoggerFactory.getLogger(DynamicRoutesFilter.class);

    @Autowired
    FilterUtils filterUtils;


    @Override
    public String filterType() {
        return filterUtils.ROUTE_FILTER_TYPE;
    }

    @Override
    public int filterOrder() {
        return FILTER_ORDER;
    }

    @Override
    public boolean shouldFilter() {
        return SHOULD_FILTER;
    }

    //带有辅助请求的方法类，spring提供
    private ProxyRequestHelper helper = new ProxyRequestHelper();


    private Header[] convertHeaders(MultiValueMap<String, String> headers) {
        List<Header> list = new ArrayList<>();
        for (String name : headers.keySet()) {
            for (String value : headers.get(name)) {
                list.add(new BasicHeader(name, value));
            }
        }
        return list.toArray(new BasicHeader[0]);
    }

    private HttpResponse forwardRequest(HttpClient httpclient, HttpHost httpHost, HttpRequest httpRequest) throws IOException {
        return httpclient.execute(httpHost, httpRequest);
    }


    private MultiValueMap<String, String> revertHeaders(Header[] headers) {
        MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
        for (Header header : headers) {
            String name = header.getName();
            if (!map.containsKey(name)) {
                map.put(name, new ArrayList<>());
            }
            map.get(name).add(header.getValue());
        }
        return map;
    }

    private InputStream getRequestBody(HttpServletRequest request) {
        InputStream requestEntity = null;
        try {
            requestEntity = request.getInputStream();
        }
        catch (IOException ex) {
            // no requestBody is ok.
        }
        return requestEntity;
    }

    //这里使用到Http工具，可使用Feign/OKHttp代替
    private HttpResponse forward(HttpClient httpclient,  String uri,
                                 HttpServletRequest request, MultiValueMap<String, String> headers,
                                 MultiValueMap<String, String> params, InputStream requestEntity) throws Exception {
        String requestMethod = request.getMethod().toUpperCase();

         this.helper.debug(requestMethod, uri, headers, params,  requestEntity);

        URL host = new URL( uri );
        HttpHost httpHost = new HttpHost(host.getHost(), host.getPort(), host.getProtocol()) ;

        HttpRequest httpRequest;
        int contentLength = request.getContentLength();
        InputStreamEntity entity = new InputStreamEntity(requestEntity, contentLength,
                request.getContentType() != null
                        ? ContentType.create(request.getContentType()) : null);
        switch (requestMethod ) {
            case "POST":
                HttpPost httpPost = new HttpPost(uri);
                httpRequest = httpPost;
                httpPost.setEntity(entity);
                break;
            case "PUT":
                HttpPut httpPut = new HttpPut(uri);
                httpRequest = httpPut;
                httpPut.setEntity(entity);
                break;
            case "PATCH":
                HttpPatch httpPatch = new HttpPatch(uri );
                httpRequest = httpPatch;
                httpPatch.setEntity(entity);
                break;
            default:
                httpRequest = new BasicHttpRequest(requestMethod, uri);

        }
        try {
            httpRequest.setHeaders(convertHeaders(headers));
            return forwardRequest(httpclient, httpHost, httpRequest);
        } finally {
        }
    }



    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        Random r = new Random();
        int randomNum = r.nextInt(10) + 4;
        //随机引流到新版本
        if(randomNum>5){
            String URI =  ctx.getRequest().getRequestURI();
            //修改为新版本的接口地址
            String routeURI =  URI.substring(URI.indexOf("/v1")).replaceAll("v1", "v2");
            //这里使用数据库或Redis代替比较好
            String serviceAddress = "http://127.0.0.1";
            if(routeURI.contains("user")){
                serviceAddress = serviceAddress+":8083" ;
            }else {
                serviceAddress = serviceAddress+":8082" ;
            }
            routeURI = serviceAddress+routeURI;
            logger.debug(" fresh route URI : "+routeURI);
            forwardToSpecialRoute(routeURI);
        }
        return null;
    }

    private void forwardToSpecialRoute(String route) {
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();

        //创建请求首部副本
        MultiValueMap<String, String> headers = this.helper.buildZuulRequestHeaders(request);
        //创建请求参数副本
        MultiValueMap<String, String> params = this.helper.buildZuulRequestQueryParams(request);
        //创建替换端点（新端点）的请求主体副本
        InputStream requestEntity = getRequestBody(request);
        if (request.getContentLength() < 0) {
            context.setChunkedRequestBody();
        }

        this.helper.addIgnoredHeaders();
        HttpResponse response = null;
        try(CloseableHttpClient httpClient = HttpClients.createDefault()) {
            //发起请求引流到新版本端点
            response = forward(httpClient,  route, request, headers,
                    params, requestEntity);
            //将调用请求的结果设置回Zuul服务器（不然会默认返回旧版本的响应结果）
            String responseData = CharStreams.toString(new InputStreamReader(response.getEntity().getContent() , "UTF-8"));
            context.setResponseBody(responseData);
        } catch (Exception ex ) {
            ex.printStackTrace();

        }
    }
}
